var __createBinding =
    (this && this.__createBinding) ||
    (Object.create
        ? (o, m, k, k2) => {
              if (k2 === undefined) k2 = k;
              var desc = Object.getOwnPropertyDescriptor(m, k);
              if (
                  !desc ||
                  ("get" in desc
                      ? !m.__esModule
                      : desc.writable || desc.configurable)
              ) {
                  desc = { enumerable: true, get: () => m[k] };
              }
              Object.defineProperty(o, k2, desc);
          }
        : (o, m, k, k2) => {
              if (k2 === undefined) k2 = k;
              o[k2] = m[k];
          });
var __setModuleDefault =
    (this && this.__setModuleDefault) ||
    (Object.create
        ? (o, v) => {
              Object.defineProperty(o, "default", {
                  enumerable: true,
                  value: v,
              });
          }
        : (o, v) => {
              o.default = v;
          });
var __importStar =
    (this && this.__importStar) ||
    ((mod) => {
        if (mod?.__esModule) return mod;
        var result = {};
        if (mod != null)
            for (var k in mod)
                if (k !== "default" && Object.hasOwn(mod, k))
                    __createBinding(result, mod, k);
        __setModuleDefault(result, mod);
        return result;
    });
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProxyObject = exports.isProxyObjectExpression = void 0;
const t = __importStar(require("@babel/types"));
const misc_1 = require("../../helpers/misc");
const proxyFunction_1 = require("../proxyFunctions/proxyFunction");
/**
 * Returns whether a node is a proxy object.
 * @param node The node.
 * @returns Whether.
 */
const isProxyObjectExpression = (node) => {
    return t.isObjectExpression(node) && node.properties.length > 0;
};
exports.isProxyObjectExpression = isProxyObjectExpression;
class ProxyObject {
    /**
     * Creates a new proxy object.
     * @param variable The variable.
     */
    constructor(variable) {
        this.literalProperties = new Map();
        this.proxyFunctionProperties = new Map();
        this.variable = variable;
    }
    /**
     * Finds all the object's entries which can be replaced.
     */
    process() {
        for (const property of this.variable.expression.properties) {
            if (
                t.isObjectProperty(property) &&
                this.isLiteralPropertyKey(property)
            ) {
                const key = t.isIdentifier(property.key)
                    ? property.key.name
                    : property.key.value;
                if (t.isLiteral(property.value)) {
                    this.literalProperties.set(key, property.value);
                } else if (
                    (0, proxyFunction_1.isProxyFunctionExpression)(
                        property.value,
                    )
                ) {
                    const proxyFunction = new proxyFunction_1.ProxyFunction(
                        property.value,
                    );
                    this.proxyFunctionProperties.set(key, proxyFunction);
                }
            } else if (
                t.isObjectMethod(property) &&
                this.isLiteralMethodKey(property)
            ) {
                const key = t.isIdentifier(property.key)
                    ? property.key.name
                    : property.key.value;
                if ((0, proxyFunction_1.isProxyFunctionExpression)(property)) {
                    const proxyFunction = new proxyFunction_1.ProxyFunction(
                        property,
                    );
                    this.proxyFunctionProperties.set(key, proxyFunction);
                }
            }
        }
    }
    /**
     * Returns the usages of the object.
     * @returns The usages.
     */
    getUsages() {
        return this.variable.binding.referencePaths;
    }
    /**
     * Attempts to replace a usage of the object.
     * @param path The path of the usage.
     * @returns Whether it was replaced.
     */
    replaceUsage(path) {
        const parentPath = path.parentPath;
        if (
            parentPath?.isMemberExpression() &&
            this.isLiteralMemberKey(parentPath.node) &&
            (!parentPath.parentPath ||
                !parentPath.parentPath.isAssignmentExpression() ||
                parentPath.parentKey !== "left")
        ) {
            const key = t.isIdentifier(parentPath.node.property)
                ? parentPath.node.property.name
                : parentPath.node.property.value;
            if (this.literalProperties.has(key)) {
                const value = this.literalProperties.get(key);
                parentPath.replaceWith((0, misc_1.copyExpression)(value));
                return true;
            } else if (
                parentPath.parentPath?.isCallExpression() &&
                parentPath.key === "callee" &&
                this.proxyFunctionProperties.has(key)
            ) {
                const proxyFunction = this.proxyFunctionProperties.get(key);
                const replacement = proxyFunction.getReplacement(
                    parentPath.parentPath.node.arguments,
                );
                parentPath.parentPath.replaceWith(replacement);
                return true;
            }
        }
        return false;
    }
    /**
     * Returns whether an object property has a literal key.
     * @param property The object property.
     * @returns Whether.
     */
    isLiteralPropertyKey(property) {
        return (
            t.isStringLiteral(property.key) ||
            t.isNumericLiteral(property.key) ||
            (!property.computed && t.isIdentifier(property.key))
        );
    }
    /**
     * Returns whether an object method has a literal key.
     * @param property The object method.
     * @returns Whether.
     */
    isLiteralMethodKey(property) {
        return (
            t.isStringLiteral(property.key) ||
            t.isNumericLiteral(property.key) ||
            (!property.computed && t.isIdentifier(property.key))
        );
    }
    /**
     * Returns whether a member expression has a literal key.
     * @param member The member expression.
     * @returns Whether.
     */
    isLiteralMemberKey(member) {
        return (
            t.isStringLiteral(member.property) ||
            t.isNumericLiteral(member.property) ||
            (!member.computed && t.isIdentifier(member.property))
        );
    }
}
exports.ProxyObject = ProxyObject;
